home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Snippets / Networking / TCPClose / closer.c next >
Encoding:
C/C++ Source or Header  |  1992-11-16  |  6.7 KB  |  289 lines  |  [TEXT/KAHL]

  1. /********************************************************************************
  2.  
  3.  TCPClose
  4.  
  5.  this snippet shows how to close a connection gracefully without causing errors or forcing
  6.  aborts on either the local or remote end.
  7.  
  8.  note: don't run this from the think debugger, as it doesn't like getting invoked at
  9.        interrupt time via a DebugStr()...
  10.        
  11.  Steve Falkenburg
  12.  MacDTS
  13.  11/16/92
  14.   
  15.  ********************************************************************************/
  16.  
  17.  
  18. #include <MacTCPCommonTypes.h>
  19. #include <TCPPB.h>
  20.  
  21. // constants
  22.  
  23. #define kTimeout         30
  24. #define kFTPServerIP    0x5a85342c            // change this to your ftp host !!
  25. #define    kFTPPort        21
  26.  
  27. // globals
  28.  
  29. short gDrvrRef;
  30. Boolean gTerminated;
  31.  
  32. // prototypes
  33.  
  34. void main(void);
  35. OSErr MakeStream(StreamPtr *stream);
  36. OSErr DoFTPConnStuff(StreamPtr stream);
  37. OSErr OpenConnection(StreamPtr stream,long address,short port);
  38. OSErr CloseConnection(StreamPtr stream);
  39. OSErr ReceiveAndJunkData(StreamPtr stream);
  40. pascal void ASR(
  41.         StreamPtr tcpStream,
  42.         unsigned short eventCode,
  43.         Ptr userDataPtr,
  44.         unsigned short terminReason,
  45.         struct ICMPReport *icmpMsg);
  46.  
  47.  
  48. // main program...
  49. //
  50. void main(void)
  51. {
  52.     OSErr err;
  53.     StreamPtr conn;
  54.         
  55.     err = OpenDriver("\p.IPP",&gDrvrRef);            // open TCP driver
  56.     if (err!=noErr) {
  57.         DebugStr("\pMacTCP not installed");
  58.         return;
  59.     }
  60.     
  61.     err = MakeStream(&conn);                        // create our connection stream
  62.     if (err!=noErr) {
  63.         DebugStr("\pcouldn't make stream");
  64.         return;
  65.     }
  66.     
  67.     err = OpenConnection(conn,kFTPServerIP,21);        // open a connection (change IP #)
  68.     if (err!=noErr) {
  69.         CloseConnection(conn);
  70.         DebugStr("\pConnection not opened");
  71.         return;
  72.     }
  73.     
  74.     err = DoFTPConnStuff(conn);                        // do a short FTP session
  75.     if (err!=noErr)
  76.         DebugStr("\pFTP server not responding");
  77.     
  78.     err = CloseConnection(conn);                    // close the connection and stream
  79. }
  80.  
  81.  
  82. // opens a TCP stream and returns the stream ptr.  we use an 8k stream buffer
  83. //
  84. OSErr MakeStream(StreamPtr *stream)
  85. {
  86.     OSErr err;
  87.     TCPiopb pBlock;
  88.     Ptr connBuffer;
  89.     long connBufferLen;
  90.     
  91.     connBufferLen = 8192;
  92.     connBuffer = NewPtr(connBufferLen);
  93.     if (MemError()!=noErr)
  94.         return MemError();
  95.         
  96.     pBlock.ioCRefNum = gDrvrRef;
  97.     pBlock.csCode = TCPCreate;
  98.     pBlock.csParam.create.rcvBuff = connBuffer;
  99.     pBlock.csParam.create.rcvBuffLen = connBufferLen;
  100.     pBlock.csParam.create.notifyProc = ASR;
  101.     err = PBControl(&pBlock,false);
  102.     
  103.     *stream = pBlock.tcpStream;
  104.     
  105.     if (err!=noErr)
  106.         DisposPtr(connBuffer);
  107.         
  108.     return err;
  109. }
  110.  
  111.  
  112. // opens the connection to the ip # and tcp port passed in
  113. //
  114.  
  115. OSErr OpenConnection(StreamPtr stream,long address,short port)
  116. {
  117.     OSErr err;
  118.     TCPiopb pBlock;
  119.  
  120.     gTerminated = false;
  121.  
  122.     pBlock.ioCRefNum = gDrvrRef;
  123.     pBlock.csCode = TCPActiveOpen;
  124.     pBlock.tcpStream = stream;
  125.     pBlock.csParam.open.ulpTimeoutValue = kTimeout;
  126.     pBlock.csParam.open.ulpTimeoutAction = 1;
  127.     pBlock.csParam.open.validityFlags = 0xC0;
  128.     pBlock.csParam.open.commandTimeoutValue = kTimeout;
  129.     pBlock.csParam.open.remoteHost = address;
  130.     pBlock.csParam.open.remotePort = port;
  131.     pBlock.csParam.open.localPort = 0;
  132.     pBlock.csParam.open.tosFlags = 0;
  133.     pBlock.csParam.open.precedence = 0;
  134.     pBlock.csParam.open.dontFrag = 0;
  135.     pBlock.csParam.open.timeToLive = 0;
  136.     pBlock.csParam.open.security = 0;
  137.     pBlock.csParam.open.optionCnt = 0;
  138.     err = PBControl((ParmBlkPtr)&pBlock,false);
  139.  
  140.     return err;        
  141. }
  142.  
  143.  
  144. // does a short FTP session, consisting of receiving the welcome message,
  145. // issuing a "QUIT" command, and receiving the response
  146. //
  147. OSErr DoFTPConnStuff(StreamPtr stream)
  148. {
  149.     OSErr err;
  150.     TCPiopb pBlock;
  151.     wdsEntry wds[2];
  152.  
  153.     wds[0].length = 6;
  154.     wds[0].ptr = "QUIT\r\n";
  155.     wds[1].length = 0;
  156.     
  157.     err = ReceiveAndJunkData(stream);
  158.     if (err!=noErr)
  159.         DebugStr("\precverror");
  160.         
  161.     pBlock.ioCRefNum = gDrvrRef;
  162.     pBlock.csCode = TCPSend;
  163.     pBlock.tcpStream = stream;
  164.     pBlock.csParam.send.ulpTimeoutValue = kTimeout;
  165.     pBlock.csParam.send.ulpTimeoutAction = 1;
  166.     pBlock.csParam.send.validityFlags = 0xC0;
  167.     pBlock.csParam.send.pushFlag = false;
  168.     pBlock.csParam.send.urgentFlag = true;
  169.     pBlock.csParam.send.wdsPtr = (Ptr)wds;
  170.     err = PBControl((ParmBlkPtr)&pBlock,false);
  171.     if (err!=noErr)
  172.         DebugStr("\perrsend");
  173.     
  174.     err = ReceiveAndJunkData(stream);
  175.     if (err!=noErr)
  176.         DebugStr("\precverror");
  177.     
  178.     return err;
  179. }
  180.  
  181.  
  182. // close the connection gracefully.  this involves issuing the TCPClose, receiving data
  183. // until we get an error (the remote end is closing) and issue a TCPRelease to get rid of
  184. // the stream.
  185. //
  186. OSErr CloseConnection(StreamPtr stream)
  187. {
  188.     OSErr err;
  189.     TCPiopb pBlock;
  190.     
  191.     pBlock.ioCRefNum = gDrvrRef;
  192.     pBlock.csCode = TCPClose;
  193.     pBlock.tcpStream = stream;
  194.     pBlock.csParam.close.ulpTimeoutValue = kTimeout;
  195.     pBlock.csParam.close.validityFlags = 0xC0;
  196.     pBlock.csParam.close.ulpTimeoutAction = 1;
  197.     err = PBControl((ParmBlkPtr)&pBlock,false);
  198.     
  199.     // receive data until the connection closes
  200.     
  201.     do {
  202.         err = ReceiveAndJunkData(stream);
  203.     } while (!gTerminated);
  204.             
  205.     pBlock.ioCRefNum = gDrvrRef;
  206.     pBlock.csCode = TCPRelease;
  207.     pBlock.tcpStream = stream;
  208.     err = PBControl((ParmBlkPtr)&pBlock,false);
  209.     
  210.     if (err!=noErr)
  211.         return err;
  212.     
  213.     DisposPtr(pBlock.csParam.create.rcvBuff);
  214.     return err;
  215. }
  216.  
  217.  
  218. // receive a block of data from the remote connection, but don't even return what the data
  219. // is.  we're using the no-copy form of the receive command for simplicity.
  220. //
  221. OSErr ReceiveAndJunkData(StreamPtr stream)
  222. {
  223.     OSErr err;
  224.     TCPiopb pBlock;
  225.     rdsEntry rds[3];
  226.     
  227.     // set up our 2-part rds
  228.     
  229.     rds[0].length = 0;
  230.     rds[0].ptr = nil;
  231.     rds[1].length = 0;
  232.     rds[1].ptr = nil;
  233.     rds[2].length = 0;
  234.     rds[2].ptr = nil;
  235.     
  236.     // issue the receive
  237.     
  238.     pBlock.ioCRefNum = gDrvrRef;
  239.     pBlock.csCode = TCPNoCopyRcv;
  240.     pBlock.tcpStream = stream;
  241.     pBlock.csParam.receive.commandTimeoutValue = kTimeout;
  242.     pBlock.csParam.receive.rdsPtr = (Ptr)rds;
  243.     pBlock.csParam.receive.rdsLength = 2;
  244.     err = PBControl((ParmBlkPtr)&pBlock,false);
  245.     
  246.     // return the buffer
  247.     
  248.     if (err==noErr) {
  249.         pBlock.csCode = TCPRcvBfrReturn;
  250.         err = PBControl((ParmBlkPtr)&pBlock,false);
  251.     }
  252.     
  253.     return err;
  254. }
  255.  
  256.  
  257. // our asynchronous notification routine.  this gets called several times, but we only
  258. // look at the connection termination messages.  we do this to determine if the close
  259. // was graceful, or if either we or the remote end had to abort.
  260. //
  261. pascal void ASR(
  262.         StreamPtr tcpStream,
  263.         unsigned short eventCode,
  264.         Ptr userDataPtr,
  265.         unsigned short terminReason,
  266.         struct ICMPReport *icmpMsg)
  267. {
  268.     if (eventCode==TCPTerminate) {
  269.         gTerminated = true;
  270.         switch (terminReason) {
  271.             case TCPULPClose:
  272.                 DebugStr("\pgraceful close on both ends");    // this is the one you want
  273.                 break;
  274.             case TCPRemoteAbort:
  275.                 DebugStr("\premote connection forced abort");
  276.                 break;
  277.             case TCPULPTimeoutTerminate:
  278.                 DebugStr("\pULP timeout reached so connection dropped");
  279.                 break;
  280.             case TCPULPAbort:
  281.                 DebugStr("\pTCPRelease issued before remote end closed");
  282.                 break;
  283.             default:
  284.                 DebugStr("\pconnection terminated non-gracefully");
  285.                 break;
  286.         }
  287.     }
  288. }
  289.